{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_crossover:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Crossover"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_crossover_sbx:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Simulated Binary Crossover (SBX)\n",
    "\n",
    "Details about the crossover can be found in <cite data-cite=\"sbx\"></cite>. Real values can be represented by a binary notation and then a the point crossovers can be performed. SBX simulated this operation by using a probability distribution *simulating* the binary crossover.\n",
    "\n",
    "A crossover object can be created by "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pymoo.operators.crossover.sbx import SBX\n",
    "\n",
    "crossover = SBX()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As arguments, the probability of a crossover and the *eta* parameter can be provided.\n",
    "\n",
    "In the example below, we demonstrate a crossover in an optimization problem with only one variable. A crossover is performed between two points, *0.2* and *0.8*, and the resulting exponential distribution is visualized. Depending on the *eta*, the exponential distribution can be fine-tuned.\n",
    "\n",
    "The probability of SBX follows an exponential distribution. Please note for demonstration purposes, we have set *prob_var=1.0*, which means every variable participates in the crossover (necessary because there exists only one variable). However, it is suggested to perform a crossover of two variables forms each parent with a probability of *0.5*, which is defined by default if not defined otherwise."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "from pymoo.core.individual import Individual\n",
    "from pymoo.core.problem import Problem\n",
    "from pymoo.operators.crossover.sbx import SBX\n",
    "\n",
    "\n",
    "def show(eta_cross):\n",
    "    problem = Problem(n_var=1, xl=0.0, xu=1.0)\n",
    "\n",
    "    a, b = Individual(X=np.array([0.2])), Individual(X=np.array([0.8]))\n",
    "\n",
    "    parents = [[a, b] for _ in range(5000)]\n",
    "\n",
    "    off = SBX(prob=1.0, prob_var=1.0, eta=eta_cross).do(problem, parents)\n",
    "    Xp = off.get(\"X\")\n",
    "\n",
    "    plt.hist(Xp, range=(0, 1), bins=200, density=True, color=\"red\")\n",
    "    plt.show()\n",
    "\n",
    "\n",
    "show(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "show(30)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Also, it can be used for integer variables. The bounds are slightly modified, and after doing the crossover, the variables are rounded."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "from pymoo.core.individual import Individual\n",
    "from pymoo.core.problem import Problem\n",
    "from pymoo.operators.crossover.sbx import SBX\n",
    "from pymoo.operators.repair.rounding import RoundingRepair\n",
    "\n",
    "\n",
    "def show(eta_cross):\n",
    "    problem = Problem(n_var=1, xl=-20, xu=20)\n",
    "\n",
    "    a, b = Individual(X=np.array([-10])), Individual(X=np.array([10]))\n",
    "\n",
    "    parents = [[a, b] for _ in range(5000)]\n",
    "\n",
    "    off = SBX(prob=1.0, prob_var=1.0, eta=eta_cross, repair=RoundingRepair(), vtype=float).do(problem, parents)\n",
    "    Xp = off.get(\"X\")\n",
    "\n",
    "    plt.hist(Xp, range=(-20, 20), bins=41, density=True, color=\"red\")\n",
    "    plt.show()\n",
    "\n",
    "\n",
    "show(3)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_crossover_point:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Point Crossover\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "from pymoo.core.individual import Individual\n",
    "from pymoo.core.problem import Problem\n",
    "from pymoo.operators.crossover.pntx import PointCrossover, SinglePointCrossover, TwoPointCrossover\n",
    "\n",
    "n_var, n_matings = 50, 30\n",
    "\n",
    "problem = Problem(n_var=n_var, xl=0.0, xu=1.0, var_type=int)\n",
    "\n",
    "a, b = Individual(X=np.arange(1, n_var + 1)), Individual(X=-np.arange(1, n_var + 1))\n",
    "\n",
    "parents = [[a, b] for _ in range(n_matings)]\n",
    "\n",
    "def show(M):\n",
    "    plt.figure(figsize=(4, 6))\n",
    "    plt.imshow(M, cmap='Greys', interpolation='nearest')\n",
    "    plt.xlabel(\"Variables\")\n",
    "    plt.ylabel(\"Individuals\")\n",
    "    plt.show()\n",
    "\n",
    "\n",
    "print(\"One Point Crossover\")\n",
    "off = SinglePointCrossover(prob=1.0).do(problem, parents)\n",
    "Xp = off.get(\"X\")\n",
    "show(Xp[:n_matings] != a.X)\n",
    "\n",
    "print(\"Two Point Crossover\")\n",
    "off = TwoPointCrossover(prob=1.0).do(problem, parents)\n",
    "Xp = off.get(\"X\")\n",
    "show(Xp[:n_matings] != a.X)\n",
    "\n",
    "print(\"K Point Crossover (k=4)\")\n",
    "off = PointCrossover(prob=1.0, n_points=4).do(problem, parents)\n",
    "Xp = off.get(\"X\")\n",
    "show(Xp[:n_matings] != a.X)\n"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_crossover_exponential:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exponential Crossover\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The exponential crossover is mostly a one-point crossover, but occasionally it can be a two-point crossover. \n",
    "First, randomly a starting index is chosen. Then, we add the next variable to be mutated with a specific probability. If we reach the last variable, we continue with the first (wrap around)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pymoo.operators.crossover.expx import ExponentialCrossover\n",
    "\n",
    "off = ExponentialCrossover(prob=1.0, prob_exp=0.9).do(problem, parents)\n",
    "Xp = off.get(\"X\")\n",
    "show((Xp[:n_matings] != a.X))"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_crossover_uniform:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Uniform Crossover\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The uniform crossover takes with a probability of 0.5 the values from each parent. \n",
    "In contrast to a point crossover, not a sequence of variables is taken, but random indices."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pymoo.operators.crossover.ux import UniformCrossover\n",
    "off = UniformCrossover(prob=1.0).do(problem, parents)\n",
    "Xp = off.get(\"X\")\n",
    "show(Xp[:n_matings] != a.X)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_crossover_half_uniform:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Half Uniform Crossover ('bin_hux', 'int_hux')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The half uniform crossover will first determine what indices are different in the first and the second parent. Then, it will take half of the difference to be selected from the other parent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pymoo.operators.crossover.hux import HalfUniformCrossover\n",
    "\n",
    "n_var, n_matings = 100, 100\n",
    "\n",
    "problem = Problem(n_var=n_var, xl=0.0, xu=1.0, var_type=int)\n",
    "\n",
    "a = Individual(X=np.full(n_var, False))\n",
    "b = Individual(X=np.array([k % 5 == 0 for k in range(n_var)]))\n",
    "\n",
    "parents = [[a, b] for _ in range(n_matings)]\n",
    "\n",
    "off = HalfUniformCrossover(prob=1.0).do(problem, parents)\n",
    "Xp = off.get(\"X\")\n",
    "show(Xp[:n_matings] != a.X)\n",
    "\n",
    "diff_a_to_b = (a.X != b.X).sum()\n",
    "diff_a_to_off = (a.X != Xp[:n_matings]).sum()\n",
    "\n",
    "print(\"Difference in bits (a to b): \", diff_a_to_b)\n",
    "print(\"Difference in bits (a to off): \", diff_a_to_off)\n",
    "print(\"Crossover Rate: \", diff_a_to_off / diff_a_to_b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### API"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. autofunction:: pymoo.factory.get_crossover\n",
    "    :noindex:\n",
    "\n",
    ".. autofunction:: pymoo.core.crossover.Crossover\n",
    "    :noindex:"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
